home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Misc Utils / Vestal Version / Source Code / VV.c < prev   
Text File  |  1993-08-02  |  12KB  |  560 lines

  1. //
  2. //    VV.c
  3. //
  4. //    Written by Alastair Rankine
  5. //    Copyright © 1993 All Rights Reserved.
  6. //    And All That Sort of Stuff.
  7. //
  8. //    Version History:
  9. //    1.0        - First Development
  10. //
  11. //    1.1        - Upgraded to DragonSmith 1.1
  12. //            - Made ProcessDoc _far_ more intelligent. Is alias aware, can recognise
  13. //            existing version numbers (even hidden amongst whitespace), and is more
  14. //            bulletproof in general.
  15. //            - Added preferences. Can now specify whether or not to quit after
  16. //            processing, whether or not to ignore aliases, and whether or not
  17. //            to try to replace existing version numbers.
  18. //            - Status dialog box. Non-modal and quite hoopy all round really.
  19. //
  20.  
  21. #include <string.h>
  22. #include <ctype.h>
  23.  
  24. #include "Dragon.h"
  25. #include "EventUtils.h"
  26.  
  27. // System 7.0 only Dialog Manager routines. These are out of technote something or other,
  28. // and not included in the standard THINK C header files.
  29. pascal OSErr GetStdFilterProc(ModalFilterProcPtr *theProc)
  30.     = { 0x303C, 0x0203, 0xAA68 };        // Returns a pointer to the Dialog Manager's standard dialog filter
  31. pascal OSErr SetDialogDefaultItem(DialogPtr theDialog, short newItem) 
  32.     = { 0x303C, 0x0304, 0xAA68 };        // Indicates to the dialog manager which item is default.  Will then alias the return
  33.                                         // & enter keys to this item, and also bold border it for you (yaaaaa!)
  34. pascal OSErr SetDialogCancelItem(DialogPtr theDialog, short newItem)
  35.     = { 0x303C, 0x0305, 0xAA68 };        // Indicates which item should be aliased to escape or Command - .
  36. pascal OSErr SetDialogTracksCursor(DialogPtr theDialog, Boolean tracks)
  37.     = { 0x303C, 0x0306, 0xAA68 };        // Tells the dialog manager that there is an edit line in this dialog, and
  38.                                         // it should track and change to an I-Beam cursor when over the edit line
  39.  
  40. // Pascal string compare function used by ProcessFile():
  41. int Pstrcmp(const unsigned char *s1, const unsigned char *s2);
  42.  
  43. // Used in the Preferences Dialog:
  44. pascal void lineProc(WindowPtr w, short item);
  45.  
  46. // Is this string a version number?
  47. char IsVersionStr(char *s);
  48.  
  49. // Item numbers for the preferences dialog:
  50. enum {
  51.     iOK = 1,
  52.     iCancel,
  53.     iQuitAfter,
  54.     iReplaceExistVNum,
  55.     iIgnoreAlias,
  56.     iTitle,
  57.     iLine
  58. };
  59.  
  60. // Item numbers for status dialog:
  61. enum {
  62.     iStop = 1,
  63.     iProcTxt,
  64.     iCurFile
  65. };
  66.  
  67. // Our preferences:
  68. enum {
  69.     prefVersPrefs = prefDragonPrefs + 1
  70. };
  71.  
  72. // Menu item number for the Preferences… dialog:
  73. const int itemPrefs = 8;
  74.  
  75. // Our dialog IDs:
  76. enum {
  77.     dlogPref = 128,
  78.     dlogStatus
  79. };
  80.  
  81. // Our preferences:
  82. struct VPrefs {
  83.     char        quitAfter;
  84.     char        replaceExistVNum;
  85.     char        ignoreAliases;
  86. };
  87.  
  88. //
  89. //    The class definition itself!
  90. //
  91. class DVers: public Dragon {
  92.     
  93.     protected:
  94.         struct VPrefs    **VersPrefs;        // The prefs stored in the prefs file
  95.         struct VPrefs    p;                    // Prefs to use at run-time.
  96.         
  97.         DialogPtr        stat;                // The status dialog, if any.
  98.         Handle            curFile;            // Handle to the static text item for the
  99.                                             // current file name.
  100.  
  101.     public:
  102.                         DVers(void);
  103.         void            ProcessFile(void);
  104.     
  105.     protected:
  106.         void            DoAbout(void);
  107.         void             AdjustMenusBusy(void);
  108.         void            AdjustMenusIdle(void);
  109.         void            DoMenu(long menuItemCode);
  110.         void            DoMouseDown(EventRecord *theEvent);
  111.         void            DoDrag(WindowPtr wind, Point p);
  112.         void            DoPrefsDialog(void);
  113.         void            ReadPrefs(void);
  114.         void            BeginProcessing(void);
  115.         void            DoEvent(EventRecord *er);
  116.         void            EndProcessing(void);
  117. };
  118.  
  119. Dragon *CreateGDragon (void)
  120. {
  121.     return (Dragon *) new DVers;
  122. }
  123.  
  124. DVers::DVers (void)
  125. {
  126.     VersPrefs = NULL;
  127.     p.quitAfter = 0;
  128.     p.replaceExistVNum = 1;
  129.     p.ignoreAliases = 0;
  130.     dirDepthLimit = -10;
  131.     autoQuit = FALSE;
  132. }
  133.  
  134. void
  135. DVers::DoAbout(void)
  136. {
  137.     Alert(128, nil);
  138. }
  139.  
  140. void
  141. DVers::ProcessFile (void)
  142. {
  143.     short    rf = 0, i;
  144.     Handle    vers;
  145.     FSSpec    copy;
  146.     OSErr    err;
  147.     
  148.     // Display the filename:
  149.     if(curFile)
  150.         SetIText(curFile, curDocFSS->name);
  151.         
  152.     if(!p.ignoreAliases) {
  153.         Boolean    fold, alias;
  154.         
  155.         // Make a copy of the FSSpec, ready to hold the FSSpec of the resolved alias file.
  156.         BlockMove(curDocFSS, ©, sizeof(FSSpec));
  157.         err = ResolveAliasFile(©, 1, &fold, &alias);
  158.         
  159.         // If it's a folder, or the alias can't be resolved, exit quietly...
  160.         if(fold || alias && (err == nsvErr || err == fnfErr || err == dirNFErr))
  161.             goto yo_later_dude;
  162.         else if(err) {
  163.             Error(err);
  164.             goto yo_later_dude;
  165.         }
  166.         
  167.         rf = FSpOpenResFile(©, fsRdPerm);
  168.     } else
  169.         rf = FSpOpenResFile(curDocFSS, fsRdPerm);
  170.  
  171.     if(rf == -1) {
  172.         err = ResError();
  173.         
  174.         // File may either have no resource fork or an empty one, in which case we
  175.         // exit quietly:
  176.         if(err && err != resFNotFound && err != eofErr)
  177.             Error(err);
  178.             
  179.         rf = 0;
  180.         goto yo_later_dude;
  181.     }
  182.     
  183.     if(vers = Get1Resource('vers', 1))    {
  184.         short    namelen = curDocFSS->name[0], verslen = (*vers)[6];
  185.         Str255    n, v;    // New name and version number, respectively.
  186.         char    *lastWd, *versNo = (char *)v;
  187.         
  188.         // Copy the version string
  189.         BlockMove(*vers + 7, v, verslen);
  190.         v[verslen] = 0;
  191.  
  192.         // Don't need the resource file any more...
  193.         CloseResFile(rf);
  194.         err = ResError();
  195.         if(err) {
  196.             Error(err);
  197.             goto yo_later_dude;
  198.         }
  199.         
  200.         // Copy the old name into n + 1 (leaving room for the length byte)
  201.         BlockMove(curDocFSS->name + 1, n + 1, namelen);
  202.         n[namelen + 1] = 0;
  203.  
  204.         // Remove trailing space on the name:
  205.         lastWd = (char *)n + namelen;
  206.         while(isspace(*lastWd) && (lastWd > (char *)n))
  207.             *lastWd-- = 0;
  208.  
  209.         // First check the version string: some applications (eg Speedometer)
  210.         // store the application name in here as well, believe it or not. What
  211.         // we do to get around this is to iterate through the string, word
  212.         // by word until a valid version string is found. If one can't be found,
  213.         // we abort.
  214.         do {
  215.             // Have we found a version string
  216.             if(IsVersionStr(versNo))
  217.                 break;
  218.  
  219.             // Go to the next word:
  220.             if(versNo = strchr(versNo, ' '))
  221.                 versNo++;
  222.  
  223.         } while(versNo);
  224.         
  225.         if(!versNo)
  226.             goto yo_later_dude;
  227.         else {
  228.             // Null-terminate the version string:
  229.             char *c = versNo;
  230.  
  231.             do
  232.                 c++;
  233.             while(*c && !isspace(*c));
  234.             
  235.             *c = 0;
  236.         }
  237.  
  238.         // Get the last word of the filename:
  239.         lastWd = strrchr((char *)n + 1, ' ');
  240.         
  241.         // If there's more than one word, we need to investigate further...
  242.         if(lastWd) {
  243.             // Reset lastWd if there was no version number:
  244.             if(!IsVersionStr(lastWd + 1))
  245.                 lastWd = NULL;
  246.                 
  247.             // Or abort if we're not to replace the one that's there:
  248.             else if(!p.replaceExistVNum)
  249.                 goto yo_later_dude;
  250.         }
  251.         
  252.         // No version number? Well set lastWd to point to where one should be!
  253.         if(!lastWd)
  254.             lastWd = (char *)n + namelen;
  255.         
  256.         // Remove any existing trailing spaces:
  257.         while(isspace(*lastWd) && (lastWd > (char *)n))
  258.             lastWd--;
  259.             
  260.         // Add a space before the new version string:
  261.         *++lastWd = ' ';
  262.         
  263.         // Append the version string.
  264.         strcpy(lastWd + 1, versNo);
  265.         
  266.         // Convert to a pascal string and check the length.
  267.         n[0] = strlen((char *)n + 1);
  268.         if(n[0] >= 63)
  269.             goto yo_later_dude;
  270.         
  271.         // Do the rename if the filename has changed:
  272.         if(Pstrcmp(curDocFSS->name, n)) {
  273.             err = FSpRename(curDocFSS, n);
  274.             if(err) {
  275.                 Error(err);
  276.                 goto yo_later_dude;
  277.             }
  278.             
  279.             // Now tell the finder about it:
  280.             BlockMove(curDocFSS, ©, sizeof(FSSpec));
  281.             CopyPStr(n, copy.name);
  282.             FSpRefreshFinderDisplay(©);
  283.         }
  284.         
  285.     } else    {
  286.         CloseResFile(rf);
  287.         err = ResError();
  288.         if(err)
  289.             Error(err);
  290.     }
  291.     
  292.     yo_later_dude:
  293.     if(rf)
  294.         CloseResFile(rf);
  295. }
  296.  
  297. void
  298. DVers::BeginProcessing(void)
  299. {
  300.     Rect r;
  301.     
  302.     inherited::BeginProcessing();
  303.     
  304.     stat = GetNewDialog(dlogStatus, nil, (WindowPtr)-1L);
  305.     if(!stat)
  306.         return;
  307.     
  308.     // Set the GrafPort
  309.     SetPort(stat);
  310.     
  311.     // Set the default buttons
  312.     SetDialogCancelItem(stat, 1);
  313.  
  314.     // Set the curFile variable;
  315.     GetDItem(stat, iCurFile, &r, &curFile, &r);
  316. }
  317.  
  318. void
  319. DVers::DoEvent(EventRecord *er)
  320. //
  321. //    Handle our modeless status dialog if neccessary.
  322. //
  323. {
  324.     if(IsDialogEvent(er)) {
  325.         int item = 0;
  326.         DialogPtr dlog = FrontWindow();
  327.             
  328.         if(er->what == keyDown)    {
  329.  
  330.             // Check for Esc or cmd-. keypresses.
  331.             if(IsCancelEvent(er))
  332.                 item = iStop;
  333.             
  334.             // Get any? (ooer!)
  335.             if(item)    {
  336.                 ControlHandle    ch;
  337.                 Rect            r;
  338.  
  339.                 // Toggle the control on and off again to give some feedback:
  340.                 GetDItem(dlog, item, &r, &ch, &r);
  341.                 HiliteControl(ch, 1);
  342.                 Delay(4, &r);
  343.                 HiliteControl(ch, 0);
  344.             }
  345.         }
  346.         
  347.         // Let the dialog manager do whatever's neccessary:
  348.         DialogSelect(er, &dlog, &item);
  349.  
  350.         // Have we aborted?
  351.         if(dlog == stat && item == iStop)
  352.             StopProcessing(noErr);
  353.  
  354.     } else    
  355.         inherited::DoEvent(er);
  356. }
  357.  
  358. void
  359. DVers::DoMouseDown(EventRecord *theEvent)
  360. {
  361.     WindowPtr    whichWindow;
  362.     short        domain;
  363.     
  364.     domain = FindWindow(theEvent->where, &whichWindow);
  365.     switch (domain) {
  366.         case inDrag:
  367.             DoDrag(whichWindow, theEvent->where);
  368.             break;
  369.         default:
  370.             inherited::DoMouseDown(theEvent);
  371.             break;
  372.     }
  373. }
  374.  
  375. void
  376. DVers::DoDrag(WindowPtr wind, Point mPoint)
  377. {
  378.     if(wind != FrontWindow())
  379.         SelectWindow(wind);
  380.     else
  381.         DragWindow(wind, mPoint, &screenBits.bounds);
  382. }
  383.  
  384. void
  385. DVers::EndProcessing(void)
  386. {
  387.     inherited::EndProcessing();
  388.     
  389.     // Set this variable if our prefs tell us to quit!
  390.     autoQuit = p.quitAfter;
  391.     
  392.     // Get rid of the status box if neccessary:
  393.     if(stat) {
  394.         DisposDialog(stat);
  395.         stat = NULL;
  396.         curFile = NULL;
  397.     }
  398. }
  399.  
  400. void
  401. DVers::DoMenu (long menuItemCode)
  402. {
  403.     short    menuID, itemNum;
  404.  
  405.     menuID = menuItemCode >> 16;
  406.     itemNum = menuItemCode & 0xFFFF;
  407.  
  408.     // Our preferences… menu item needs to be checked for...
  409.     if (menuID == mEdit && itemNum == itemPrefs)
  410.         DoPrefsDialog();
  411.     else
  412.         inherited::DoMenu (menuItemCode);
  413. }
  414.  
  415. void
  416. DVers::AdjustMenusBusy (void)
  417. {
  418.     inherited::AdjustMenusBusy ();
  419.     DisableItem (editMenu, itemPrefs);    // Disable the Preferences… item
  420. }
  421.  
  422. void
  423. DVers::AdjustMenusIdle (void)
  424. {
  425.     inherited::AdjustMenusIdle ();
  426.     EnableItem (editMenu, itemPrefs);
  427. }
  428.  
  429. void
  430. DVers::DoPrefsDialog(void)
  431. //
  432. //    Puts a modal (urk! I know!) dialog box up to edit the preferences.
  433. //
  434. {
  435.     DialogPtr        dlog;
  436.     Rect            r;
  437.     ControlHandle    CtlQuitAfter, CtlIgnoreAlias, CtlReplaceExistVNum;
  438.     int                item;
  439.     
  440.     dlog = GetNewDialog(dlogPref, nil, (WindowPtr)-1L);
  441.     if(!dlog)
  442.         return;
  443.     SetPort(dlog);
  444.  
  445.     // Set the default buttons
  446.     SetDialogDefaultItem(dlog, iOK);
  447.     SetDialogCancelItem(dlog, iCancel);
  448.  
  449.     // Set the lineProc item:
  450.     GetDItem(dlog, iLine, &item, &CtlQuitAfter, &r);
  451.     SetDItem(dlog, iLine, item, lineProc, &r);
  452.  
  453.     // Get the control handles:
  454.     GetDItem(dlog, iQuitAfter, &item, &CtlQuitAfter, &r);
  455.     GetDItem(dlog, iIgnoreAlias, &item, &CtlIgnoreAlias, &r);
  456.     GetDItem(dlog, iReplaceExistVNum, &item, &CtlReplaceExistVNum, &r);
  457.  
  458.     SetCtlValue(CtlQuitAfter, p.quitAfter);
  459.     SetCtlValue(CtlIgnoreAlias, p.ignoreAliases);
  460.     SetCtlValue(CtlReplaceExistVNum, p.replaceExistVNum);
  461.  
  462.     do    {
  463.         ControlHandle ch = NULL;
  464.         
  465.         ModalDialog(nil, &item);
  466.         switch (item)    {
  467.             case iQuitAfter:
  468.                 ch = CtlQuitAfter;
  469.                 break;
  470.             case iIgnoreAlias:
  471.                 ch = CtlIgnoreAlias;
  472.                 break;
  473.             case iReplaceExistVNum:
  474.                 ch = CtlReplaceExistVNum;
  475.                 break;
  476.             default:
  477.                 break;
  478.         }
  479.         
  480.         // Toggle the control:
  481.         if(ch)
  482.             SetCtlValue(ch, !GetCtlValue(ch));
  483.             
  484.     }    while (item != iOK && item != iCancel);
  485.  
  486.     if (item == OK)    {
  487.         p.quitAfter = GetCtlValue(CtlQuitAfter);
  488.         p.ignoreAliases = GetCtlValue(CtlIgnoreAlias);
  489.         p.replaceExistVNum = GetCtlValue(CtlReplaceExistVNum);
  490.         
  491.         // Save the preferences:
  492.         BlockMove(&p, *VersPrefs, sizeof(struct VPrefs));
  493.         preferences->SavePrefResource(prefVersPrefs);
  494.     }
  495.     
  496.     DisposDialog(dlog);
  497. }
  498.     
  499. void DVers::ReadPrefs (void)
  500. {
  501.     inherited::ReadPrefs ();
  502.  
  503.     VersPrefs = (struct VPrefs **) preferences->GetPrefResource (prefVersPrefs);
  504.     if (VersPrefs)
  505.         BlockMove(*VersPrefs, &p, sizeof(struct VPrefs));
  506. }
  507.  
  508. pascal void
  509. lineProc(WindowPtr w, short item)
  510. //
  511. //    Does a double line in the relevant user item...
  512. //
  513. {
  514.     Handle        h;
  515.     Rect        r;
  516.     
  517.     GetDItem(w, item, &h, &h, &r);
  518.     MoveTo(r.left, r.top);
  519.     LineTo(r.right, r.top);
  520.     MoveTo(r.left, r.bottom - 1);
  521.     LineTo(r.right, r.bottom - 1);
  522. }
  523.  
  524. int
  525. Pstrcmp(const unsigned char *s1, const unsigned char *s2)
  526. //
  527. //    like strcmp, only for pascal strings (!)
  528. //
  529. {
  530.     int        i = *s1;
  531.     
  532.     if (i == *s2)
  533.         for (; i; i--)
  534.             if (s1[i] != s2[i])
  535.                 return(i);
  536.     return(i);
  537. }
  538.  
  539. char
  540. IsVersionStr(char *c)
  541. //
  542. //    Returns true if the word (ie delimited by null or whitespace) pointed to by wd
  543. //    is a valid version number.
  544. //    
  545. {
  546.     char alphCount = 2;
  547.     
  548.     for(; *c && !isspace(*c) && alphCount; c++) {
  549.         // Not a version number if it has more than one alphabetic character...
  550.         if(isalpha(*c))
  551.             alphCount--;
  552.  
  553.         // Version numbers can only have digits or dots...                        
  554.         else if(!isdigit(*c) && *c != '.')
  555.             alphCount = 0;
  556.     }
  557.     
  558.     return(alphCount != 0);
  559. }
  560.